<?php

require_once('Utility.php');
require_once('Db.php');
require_once('Exceptions.php');
require_once('Smarty-3.1.13/libs/Smarty.class.php');

/**
 * 应用类。
 */
class Application {
  function __construct($_config = false, $exceptionHandler = false){
    global $__default_config;
    $this->config = $_config ? $_config : $__default_config;
    $this->exceptionHandler = $exceptionHandler ? $exceptionHandler : 'exceptionHandler';
    $this->smarty = new Smarty();
    if (!isset($this->config['cacheDir'])){
      $this->smarty->caching = Smarty::CACHING_OFF;
    } else {
      $this->smarty->setCacheDir($this->config['cacheDir']);
    }
    $this->smarty->setTemplateDir($this->config['templateDir']);
    $this->smarty->setCompileDir($this->config['compileDir']);
    if (isset($this->config['database'])){
      try {
        $dbConfig = $this->config['database'];
        $this->db = new Db($dbConfig['host'], $dbConfig['port'], 
          $dbConfig['user'], $dbConfig['password'], $dbConfig['db'],
          $dbConfig['encoding']);
      } catch (Exception $e){
        $this->onException($e);
      }
    }
  }

  function __destruct(){}

  function run(){
    $this->handle($this->getUser(), $this->getResourceId(), 
      $this->getParameters($this->getMethod()));
  }

  // 处理HTTP请求。
  function handle($user, $resourceId, $parameters){
    try {
      $handler = $this->getHandler($resourceId);
      // handler 返回true表示已经渲染，返回false表示没有渲染。
      if (!$handler($user, $parameters, $this)){
        $this->smarty->display($this->getTemplate($resourceId));
      }
    } catch (Exception $e){
      $this->onException($e);
    }
  }

  // 通过handler名字调用handler。$path是相对于handlerDir的相对路径。
  function invokeHandler($handlerName, $user, $parameters, $app, $path = ''){
    if ($handlerName == 'staticHandler'){
      return $staticHandler($user, $parameters, $app);
    }    
    $filename = $this->config['handlerDir'].$path.$handlerName.'.handler';
    require_once($filename);
    $handler = 'handler_'.$handlerName;
    return $handler($user, $parameters, $app);
  }

  // 获取处理器对象。返回处理器对象。如果获取失败，抛出异常。
  function getHandler($resourceId){
    // 是否是静态资源，比如js/css/jpeg文件。
    if ($this->isStaticResource($resourceId)){
      return "staticHandler";
    }
    // 不是静态资源，在目录中查找处理器。
    $len = strlen($resourceId);
    if ($s = strstr($resourceId, '?')){
      $resouceId = substr($resourceId, $len - strlen($s) + 1);
    }
    if ($resourceId[strlen($resourceId) - 1] == '/'){
      $resourceId .= 'index';
    }
    $resourceId = trim($resourceId, '/');
    $dir = $this->config['handlerDir'];
    $fn = $dir.$resourceId.".php";
    if (file_exists($fn)){
      if (require_once($fn)){
        if ($s = strrchr($resourceId, "/")){
          $resourceId = substr($resourceId, $len - strlen($s) + 1);
        }
        return "handler_".$resourceId;
      }            
    } 
    $message = " resourceId: ".$resourceId;
    throw new HandlerNotFoundException($message);
  }

  /** 根据$resourceId查找模板。 */
  function getTemplate($resourceId){
    // 静态资源
    if ($this->isStaticResource($resourceId)){
      return $this->config['templateDir'].'static.html';
    }
    // 动态资源
    $len = strlen($resourceId);
    if ($s = strstr($resourceId, '?')){
      $resouceId = substr($resourceId, $len - strlen($s) + 1);
    }
    if ($resourceId[strlen($resourceId) - 1] == '/'){
      $resourceId .= 'index';
    }
    $resourceId = trim($resourceId, '/');
    $dir = $this->config['templateDir'];
    $fn = $dir.$resourceId.'.html';
    if (file_exists($fn)){
      return $fn;
    } 
    $message = 'resourceId: '.$resourceId.' filename: '.$fn;
    throw new TemplateNotFoundException($message);
  }

  /** 从cookie获取用户标识。返回字符串或者false。 */
  function getUser(){
    /** @todo 使用SESSION。 */
    return false;
  }

  /** 获取HTTP请求方法。返回方法字符串或者false。 */
  function getMethod(){
    if (isset($_SERVER["REQUEST_METHOD"])){
      return $_SERVER["REQUEST_METHOD"];
    }
    return false;
  }

  /** 获得资源标识，通常是URL。返回字符串或则false。 */
  function getResourceId(){
    $uri = $_SERVER['REQUEST_URI'];
    $baseUri = isset($this->config['baseUri']) ? $this->config['baseUri'] : '';
    $baseUri = trim($baseUri, '/');
    if (($len = strlen($baseUri)) > 0){
      if ($_uri = strstr($uri, $baseUri)){
        $uri = substr($_uri, $len);
      }
    }
    // 去掉URL中的GET参数。
    $len = strlen($uri);
    if ($s = strstr($uri, '?')){
      $uri = substr($uri, 0, $len - strlen($s));
    }
    return $uri;
  }

  /**
   * 获得参数。返回一个数组。
   */
  function getParameters($method){
    switch ($method){
    case "GET":
      return $_GET;
    case "POST":
      return $_POST;
    default:
      return array();
    }
  }

  /**
   * 处理异常
   */
  function onException($exception){
    $function = $this->exceptionHandler;
    $function($exception);
  }

  // 判断$app是否使用了缓存
  function useCache(){
    return (isset($this->config['cacheDir']) && strlen($this->config['cacheDir']) > 0);
  }

  // 判断是否是静态资源。
  function isStaticResource($resourceId){
    $suffix = getFileSuffix($resourceId);
    if (strlen($suffix) > 0 && strstr($this->config['staticResources'], $suffix)){
      return true;
    }
    return false;
  }
  
  // 返回静态文件存放目录。
  function getStaticDir(){
    return $this->config['staticDir'];
  }

  // 返回配置。
  function getConfig(){
    return $this->config;
  }

  // 返回缓存。
  function getCache(){
    return $this->cache;
  }

  // 返回Db对象。
  function getDb(){
    return $this->db;
  }

  // 返回Smarty对象。
  function getSmarty(){
    return $this->smarty;
  }

  private $exceptionHandler;
  private $smarty;
  private $config;
  private $cache;
  private $db;
}

?>
